Conditional forecasting using relative entropy ============================================== Besides the model-based conditional forecasts -- where conditions are achieved through the model's own structural shocks (the ``conditions`` argument to ``forecast``; see the :doc:`reduced-form VAR chapter `, and, for perfect-foresight conditioning, the :doc:`simulation-plan object `) -- RISE can impose conditions by **reweighting** an already-computed predictive distribution. This is *entropic tilting* (relative-entropy / exponential tilting), in the tradition of Robertson, Tallman and Whiteman, of Cogley, Morozov and Sargent, and of Krüger, Clark and Ravazzolo. The idea -------- Start from a *sample* of forecast paths -- :math:`N` draws, for every variable, over the forecast horizon -- carrying equal weights :math:`\pi_i = 1/N`. You have additional information you want to impose, expressed as a moment condition: some function :math:`g(\cdot)` of a path should average to a target :math:`\bar c`. Entropic tilting replaces the equal weights by new weights :math:`\omega_i` that are as close as possible -- in Kullback-Leibler distance -- to the originals, subject to that condition: .. math:: \min_{\omega}\;\sum_i \omega_i \log\frac{\omega_i}{\pi_i} \quad\text{s.t.}\quad \sum_i \omega_i g(x_i) = \bar c,\;\; \sum_i \omega_i = 1. The solution is the exponential tilt :math:`\omega_i \propto \pi_i\,\exp\!\left(\gamma' g(x_i)\right)`, where the multiplier :math:`\gamma` is chosen so the condition holds. The reweighted draws are then summarised as usual (means, quantiles, fan charts) -- nothing about the model or its shocks changes; you have simply moved probability mass toward the paths consistent with your information. When to use it -------------- - The condition is **distributional** -- a target mean, a quantile, a variance -- rather than an exact path value; - you want to bring in **external information** (a survey, a judgmental view, an off-model forecast) that is not naturally expressed through the model's shocks; - you already have the predictive *sample* (it need not even come from a RISE model) and want a cheap reweighting rather than a fresh model solve. The price is that the conditions hold only *in expectation under the tilted measure*, and there is no structural-shock decomposition of the result -- if you need exact conditioning with a shock interpretation, use the model-based route. The API ------- The high-level entry point is ``utils.entropic_tilting.tilt_scenario``:: [omega, fkst_tilted] = utils.entropic_tilting.tilt_scenario(fkst, mfun, c, options); - ``fkst`` -- a structure of simulated paths, one field per variable, each a ``T x N`` matrix of draws (the kind of object ``forecast`` / ``simulate`` return when given parameter draws); - ``mfun`` -- a function handle; ``mfun(X)`` returns the moment(s) of interest from one path ``X`` (a variables-by-horizon array); - ``c`` -- the target value(s) :math:`\bar c` for those moments; - ``options`` -- options passed through to the tilting solver (``utils.entropic_tilting.core``). It returns ``omega`` (the ``N x 1`` vector of tilted weights) and a forecast structure with the reweighted statistics; pass ``omega`` to the fan-chart helpers (or compute your own weighted quantiles) to display the tilted forecast alongside the untilted one. A sketch -------- Suppose you want to tilt a VAR forecast distribution toward a path on which a Taylor-type policy-rule residual is zero on average -- i.e. "the central bank broadly follows its rule over the forecast":: % 1. an unconditional forecast distribution (parameter draws -> paths) params = mest.estim_.sampler(2000); fkst = forecast(mest, db, '2024Q1', params, 12); % fields: var -> T x N % 2. the moment to condition on: the per-period rule residual % R{t} - rho*R{t-1} - (1-rho)*(dpai*PAI{t} + dy*YGAP{t}) rho = 0.8; dpai = 1.5; dy = 0.3; mfun = @(X) rule_residual(X, rho, dpai, dy); % returns a vector of residuals c = 0; % target: zero on average % 3. tilt [omega, fkst_tilted] = utils.entropic_tilting.tilt_scenario(fkst, mfun, c); % 4. fan charts, tilted vs untilted plot_fanchart(fanchart(fkst.PAI, [30 50 68 90])) plot_fanchart(fanchart(fkst.PAI, [30 50 68 90], omega)) % weighted Solver options -------------- ``utils.entropic_tilting.tilt_scenario`` passes its ``options`` argument through to ``utils.entropic_tilting.core``, which solves for the Lagrange multiplier :math:`\gamma` with ``fsolve``. The accepted fields: ============= =========================================== ===================== Field Meaning Default ============= =========================================== ===================== ``tol`` ``fsolve`` ``TolFun`` (convergence on the ``1e-8`` moment residual) ``maxiter`` ``fsolve`` ``MaxIter`` ``500`` ``lambda0`` initial multiplier guess, ``k x 1`` ``zeros(k, 1)`` (where ``k`` is the number of moment conditions) ============= =========================================== ===================== A non-default ``lambda0`` is useful for sequential or rolling tilting where the previous solution is a warm-start for the next. Companion helper ~~~~~~~~~~~~~~~~ ``utils.entropic_tilting.weighted_quantile(x, omega, q)`` computes weighted quantiles of a sample ``x`` under weights ``omega``. Use it to build the tilted summary statistics that feed the fan-chart helper when ``plot_fanchart`` is called with an explicit ``omega``. Worked example shipped with the toolbox --------------------------------------- A runnable end-to-end example is in the toolbox repository at ``development/tests/models/var/panel/panel/tut13_conditional_forecast_entropic_tilting.m``, with companion notes in ``entropic_tilting.pdf`` next to it. The script fits a small VAR, draws an unconditional forecast distribution, and tilts it toward a Taylor-rule residual of zero on average -- exactly the pattern sketched above. The five steps in the script (``%% housekeeping`` → ``%% conditional forecast distribution`` → fan charts without tilting → with tilting → side-by-side comparison) are a template to adapt for any model. To impose other condition types (quantile targets, an external mean forecast, multiple moments at once), change only ``mfun`` and ``c``: - **Point mean target** on variable ``Yj`` at horizon ``h``: ``mfun = @(X) X(j, h);`` ``c = target_value;`` - **Quantile target** (e.g. "median of inflation over the next 8 quarters is 2%"): build ``mfun`` from an indicator :math:`\mathbf{1}\{X_{j,h} \le q\}` and target the desired probability level for ``c`` (see the entropic-tilting references below for the derivation). - **External forecast** (e.g. a survey mean for ``Yj`` at horizons ``1:H``): stack ``mfun(X) = X(j, 1:H).'`` and set ``c`` to the vector of survey means. References ---------- The entropic-tilting / relative-entropy reweighting framework used here: - Robertson, J. C., Tallman, E. W., & Whiteman, C. H. (2005). "Forecasting using relative entropy." *Journal of Money, Credit and Banking*, 37(3), 383-401. - Cogley, T., Morozov, S., & Sargent, T. J. (2005). "Bayesian fan charts for U.K. inflation: Forecasting and sources of uncertainty in an evolving monetary system." *Journal of Economic Dynamics and Control*, 29(11), 1893-1925. - Krüger, F., Clark, T. E., & Ravazzolo, F. (2017). "Using entropic tilting to combine BVAR forecasts with external nowcasts." *Journal of Business & Economic Statistics*, 35(3), 470-485.